library(data.table)
library(lubridate)
library(stringi)
library(stringr)
library(lfe)
library(car)
library(sandwich)
library(lmtest)
library(margins)
library(prediction)
library(ggplot2)
library(ggthemes)
library(scales)
library(stargazer)
# NB: stargazer should be installed with this fix: https://gist.github.com/alexeyknorre/b0780836f4cec04d41a863a683f91b53
library(showtext)
library(MatchIt)
library(optmatch)
library(cobalt)

# Declare working directory beforehand in an environment variable
# IMPERIAL_LEGAL_POLITICS_REPLICATION_PATH = "path_to_your_folder"
# with the aid of usethis::edit_r_environ()
# Restart R session for the changes to take effect
path <- Sys.getenv("IMPERIAL_LEGAL_POLITICS_REPLICATION_PATH")
setwd(path)

# Load an object with case-instance-side-outcome-level data
load("data/crimea_case_outcomes_side_instance.rdata")

# Font for plotting
font_name <- "Arial"
showtext_auto()

##############################
# Prepare the data

# Create government win by instance object
government_win_instance <- crimea_case_outcomes_side_instance[involved_government == 1 & was_appealed == 1 & government == 1, list(government_win = max(win, na.rm = T)), by = c("caseid", "instanceLevel") ]

# Create federal agency win by instance object
federal_agency_win_instance <- crimea_case_outcomes_side_instance[involved_federal_agency == 1 & was_appealed == 1 & federal_agency == 1, list(federal_agency_win = max(win, na.rm = T)), by = c("caseid", "instanceLevel") ]

# Create regional/municipal agency win by instance object
regional_municipal_agency_win_instance <- crimea_case_outcomes_side_instance[ involved_regional_municipal_agency == 1 & was_appealed == 1 & ( regional_agency == 1 | municipal_agency == 1), list(regional_municipal_agency_win = max(win, na.rm = T)), by = c("caseid", "instanceLevel") ]

# Create local entity win (private disputes) by instance object
local_entity_win_instance <- crimea_case_outcomes_side_instance[ involved_government == 0 & local_entities_only == 0 & was_appealed == 1 & local_entity == 1, list(local_entity_win = max(win, na.rm = T)), by = c("caseid", "instanceLevel") ]

# Attach case characteristics of interest
case_chars <- c("caseid", "instanceLevel", "appeal", "presiding_judge_new", "presiding_judge_russian", "any_judge_new", "any_judge_russian", "year", "involved_federal_agency", "involved_regional_agency", "involved_municipal_agency", "involved_local_entity", "government_plaintiff", "federal_agency_plaintiff", "regional_agency_plaintiff", "municipal_agency_plaintiff", "local_entity_plaintiff", "caseTypeCode", "caseCategoryUnified", "caseCategoryRedux", "petty_case", "claimSum_deflated_wins", "presiding_judge", "countDocumentsByCourt", "days_elapsed", "local_entities_only", "dispute_type")

government_win_instance <- merge(government_win_instance, unique(crimea_case_outcomes_side_instance[, c(case_chars), with = F], by = c("caseid", "instanceLevel")), by = c("caseid", "instanceLevel"), all.x = T, all.y = F)
federal_agency_win_instance <- merge(federal_agency_win_instance, unique(crimea_case_outcomes_side_instance[, c(case_chars), with = F], by = c("caseid", "instanceLevel")), by = c("caseid", "instanceLevel"), all.x = T, all.y = F)
regional_municipal_agency_win_instance <- merge(regional_municipal_agency_win_instance, unique(crimea_case_outcomes_side_instance[, c(case_chars), with = F], by = c("caseid", "instanceLevel")), by = c("caseid", "instanceLevel"), all.x = T, all.y = F)
local_entity_win_instance <- merge(local_entity_win_instance, unique(crimea_case_outcomes_side_instance[, c(case_chars), with = F], by = c("caseid", "instanceLevel")), by = c("caseid", "instanceLevel"), all.x = T, all.y = F)

# Impute 10 missing observations with days elapsed with their mean value
mean_days_elapsed_first <- mean(government_win_instance[ petty_case == 0 & instanceLevel == "1_first_instance"]$days_elapsed, na.rm = T)
mean_days_elapsed_appeal <- mean(government_win_instance[ petty_case == 0 & instanceLevel == "2_appeal"]$days_elapsed, na.rm = T)

government_win_instance[is.na(days_elapsed) & instanceLevel == "1_first_instance", days_elapsed := mean_days_elapsed_first]
government_win_instance[is.na(days_elapsed) & instanceLevel == "2_appeal", days_elapsed := mean_days_elapsed_appeal]

# Impute 6 missing observations with days elapsed with their mean value
mean_days_elapsed_first <- mean(federal_agency_win_instance[ petty_case == 0 & instanceLevel == "1_first_instance"]$days_elapsed, na.rm = T)
mean_days_elapsed_appeal <- mean(federal_agency_win_instance[ petty_case == 0 & instanceLevel == "2_appeal"]$days_elapsed, na.rm = T)

federal_agency_win_instance[is.na(days_elapsed) & instanceLevel == "1_first_instance", days_elapsed := mean_days_elapsed_first]
federal_agency_win_instance[is.na(days_elapsed) & instanceLevel == "2_appeal", days_elapsed := mean_days_elapsed_appeal]

# Impute 5 missing observations with days elapsed with their mean value
mean_days_elapsed_first <- mean(regional_municipal_agency_win_instance[ petty_case == 0 & instanceLevel == "1_first_instance"]$days_elapsed, na.rm = T)
mean_days_elapsed_appeal <- mean(regional_municipal_agency_win_instance[ petty_case == 0 & instanceLevel == "2_appeal"]$days_elapsed, na.rm = T)

regional_municipal_agency_win_instance[is.na(days_elapsed) & instanceLevel == "1_first_instance", days_elapsed := mean_days_elapsed_first]
regional_municipal_agency_win_instance[is.na(days_elapsed) & instanceLevel == "2_appeal", days_elapsed := mean_days_elapsed_appeal]

# Impute 4 missing observations with days elapsed with their mean value
mean_days_elapsed_first <- mean(local_entity_win_instance[ petty_case == 0 & instanceLevel == "1_first_instance"]$days_elapsed, na.rm = T)
mean_days_elapsed_appeal <- mean(local_entity_win_instance[ petty_case == 0 & instanceLevel == "2_appeal"]$days_elapsed, na.rm = T)

local_entity_win_instance[is.na(days_elapsed) & instanceLevel == "1_first_instance", days_elapsed := mean_days_elapsed_first]
local_entity_win_instance[is.na(days_elapsed) & instanceLevel == "2_appeal", days_elapsed := mean_days_elapsed_appeal]

# Rename instance levels
levels(government_win_instance$instanceLevel) <- levels(federal_agency_win_instance$instanceLevel) <- levels(regional_municipal_agency_win_instance$instanceLevel) <- levels(local_entity_win_instance$instanceLevel) <- c("a) First instance", "b) Appeals", "c) Cassation", "d) Supervision", "e) Supreme court")

##############################
# Clustered Optimal Full Matching
# Treatment: Russian judge
# Cluster: instance level

# Clustered full matching
government_win_match <- matchit(any_judge_russian ~ instanceLevel*(claimSum_deflated_wins + countDocumentsByCourt + days_elapsed + caseCategoryRedux), data = government_win_instance[ petty_case == 0], replace = F, exact = "instanceLevel", method = "full")
federal_agency_win_match <- matchit(any_judge_russian ~ instanceLevel*(claimSum_deflated_wins + countDocumentsByCourt + days_elapsed + caseCategoryRedux), data = federal_agency_win_instance[ petty_case == 0], replace = F, exact = "instanceLevel", method = "full")
regional_municipal_agency_win_match <- matchit(any_judge_russian ~ instanceLevel*(claimSum_deflated_wins + countDocumentsByCourt + days_elapsed + caseCategoryRedux), data = regional_municipal_agency_win_instance[ petty_case == 0], replace = F, exact = "instanceLevel", method = "full")
local_entity_win_match <- matchit(any_judge_russian ~ instanceLevel*(claimSum_deflated_wins + countDocumentsByCourt + days_elapsed + caseCategoryRedux), data = local_entity_win_instance[ petty_case == 0], replace = F, exact = "instanceLevel", method = "full")

##############################
# Balance test: first-instance vs. appellate rulings
# and "Ukrainian" vs Russian judges

# Government vs Private cases
## Table with standardized means before and after matching
balance_table_government <- cobalt::bal.tab(government_win_match, cluster = "instanceLevel", , which.cluster = .all, cluster.fun = "mean", un = TRUE)
balance_table_government_vars <- var.names(balance_table_government)
balance_table_government_vars <- gsub("caseCategoryRedux_", "", balance_table_government_vars)
balance_table_government_vars <- gsub("claimSum_deflated_wins", "Claim sum", balance_table_government_vars)
balance_table_government_vars <- gsub("days_elapsed", "Days to decision", balance_table_government_vars)
balance_table_government_vars <- gsub("countDocumentsByCourt", "Court documents", balance_table_government_vars)

## Plot with standardized means before and after matching
balance_plot_government <- cobalt::love.plot(government_win_match, cluster = "instanceLevel", stats = c("mean.diffs"), thresholds = c(m = .1), which.cluster = .all, binary = "std", wrap = 50, var.names = balance_table_government_vars, position = "bottom", themes = theme_minimal(), title = "")
balance_plot_government <- balance_plot_government + theme(legend.position = "bottom", legend.title = element_blank(), text = element_text(family = font_name, size = 14), legend.text = element_text(size = 16))
#ggsave(balance_plot_government, file = "figures/balance_plot_government.pdf", device = cairo_pdf, width = 8.75, height = 9)

# Local_entity vs non-local cases
## Table with standardized means before and after matching
balance_table_local_entity <- cobalt::bal.tab(local_entity_win_match, cluster = "instanceLevel", , which.cluster = .all, cluster.fun = "mean", un = TRUE)
balance_table_local_entity_vars <- var.names(balance_table_local_entity)
balance_table_local_entity_vars <- gsub("caseCategoryRedux_", "", balance_table_local_entity_vars)
balance_table_local_entity_vars <- gsub("claimSum_deflated_wins", "Claim sum", balance_table_local_entity_vars)
balance_table_local_entity_vars <- gsub("days_elapsed", "Days to decision", balance_table_local_entity_vars)
balance_table_local_entity_vars <- gsub("countDocumentsByCourt", "Court documents", balance_table_local_entity_vars)

## Plot with standardized means before and after matching
balance_plot_local_entity <- cobalt::love.plot(local_entity_win_match, cluster = "instanceLevel", stats = c("mean.diffs"), thresholds = c(m = .1), which.cluster = .all, binary = "std", wrap = 50, var.names = balance_table_local_entity_vars, position = "bottom", themes = theme_minimal(), title = "")
balance_plot_local_entity <- balance_plot_local_entity + theme(legend.position = "bottom", legend.title = element_blank(), text = element_text(family = font_name, size = 14), legend.text = element_text(size = 16))
#ggsave(balance_plot_local_entity, file = "figures/balance_plot_local_entity.pdf", device = cairo_pdf, width = 8.75, height = 9)

##############################
# Within-case regressions on matched data

# Extract matched data
government_win_instance_matched <- as.data.table(match.data(government_win_match))
federal_agency_win_instance_matched <- as.data.table(match.data(federal_agency_win_match))
regional_municipal_agency_win_instance_matched <- as.data.table(match.data(regional_municipal_agency_win_match))
local_entity_win_instance_matched <- as.data.table(match.data(local_entity_win_match))

##############################
# Fit TWFE regressions
# for government advantage

government_twfe_formula_basic_russianjudge <- as.formula("government_win ~ any_judge_russian + appeal | caseid | 0 | presiding_judge")
government_twfe_formula_interacted_russianjudge <- as.formula("government_win ~ any_judge_russian*appeal | caseid | 0 | presiding_judge")

## All non-petty cases
fit_government_twfe_russianjudge_basic_nonpetty_matched <- felm(government_twfe_formula_basic_russianjudge, data = government_win_instance_matched, weights = government_win_instance_matched$weights)
fit_government_twfe_russianjudge_interacted_nonpetty_matched <- felm(government_twfe_formula_interacted_russianjudge, data = government_win_instance_matched, weights = government_win_instance_matched$weights)

##############################
# Fit TWFE regressions
# for local entity advantage

local_entity_twfe_formula_basic_russianjudge <- as.formula("local_entity_win ~ any_judge_russian + appeal | caseid | 0 | presiding_judge")
local_entity_twfe_formula_interacted_russianjudge <- as.formula("local_entity_win ~ any_judge_russian*appeal | caseid | 0 | presiding_judge")

# All nonpetty cases
fit_local_entity_twfe_russianjudge_basic_nonpetty_matched <- felm(local_entity_twfe_formula_basic_russianjudge, data = local_entity_win_instance_matched, weights = local_entity_win_instance_matched$weights)
fit_local_entity_twfe_russianjudge_interacted_nonpetty_matched <- felm(local_entity_twfe_formula_interacted_russianjudge, data = local_entity_win_instance_matched, weights = local_entity_win_instance_matched$weights)

##############################
# Fit TWFE regressions
# for government advantage at different
# levels of government

###
# Federal agencies
federal_agency_twfe_formula_basic_russianjudge <- as.formula("federal_agency_win ~ any_judge_russian + appeal | caseid | 0 | presiding_judge")
federal_agency_twfe_formula_interacted_russianjudge <- as.formula("federal_agency_win ~ any_judge_russian*appeal | caseid | 0 | presiding_judge")

## All non-petty cases
fit_federal_agency_twfe_russianjudge_basic_nonpetty_matched <- felm(federal_agency_twfe_formula_basic_russianjudge, data = federal_agency_win_instance_matched, weights = federal_agency_win_instance_matched$weights)
fit_federal_agency_twfe_russianjudge_interacted_nonpetty_matched <- felm(federal_agency_twfe_formula_interacted_russianjudge, data = federal_agency_win_instance_matched, weights = federal_agency_win_instance_matched$weights)

###
# Regional/municipal agencies
regional_municipal_agency_twfe_formula_basic_russianjudge <- as.formula("regional_municipal_agency_win ~ any_judge_russian + appeal | caseid | 0 | presiding_judge")
regional_municipal_agency_twfe_formula_interacted_russianjudge <- as.formula("regional_municipal_agency_win ~ any_judge_russian*appeal | caseid | 0 | presiding_judge")

## All non-petty cases
fit_regional_municipal_agency_twfe_russianjudge_basic_nonpetty_matched <- felm(regional_municipal_agency_twfe_formula_basic_russianjudge, data = regional_municipal_agency_win_instance_matched, weights = regional_municipal_agency_win_instance_matched$weights)
fit_regional_municipal_agency_twfe_russianjudge_interacted_nonpetty_matched <- felm(regional_municipal_agency_twfe_formula_interacted_russianjudge, data = regional_municipal_agency_win_instance_matched, weights = regional_municipal_agency_win_instance_matched$weights)

##############################
# TABLE S11. Table S11: Within-case advantage regression on matched sample: Government vs. Private disputes, Federal agency vs. Private disputes, Regional/Municipal agency vs. Private disputes, and Private disputes where one of the parties is not local, Crimea courts of first instance or appellate courts, 2014–2019, non-petty cases only

table_twfe_all_matched <- stargazer(fit_government_twfe_russianjudge_basic_nonpetty_matched, fit_government_twfe_russianjudge_interacted_nonpetty_matched, fit_federal_agency_twfe_russianjudge_basic_nonpetty_matched, fit_federal_agency_twfe_russianjudge_interacted_nonpetty_matched, fit_regional_municipal_agency_twfe_russianjudge_basic_nonpetty_matched, fit_regional_municipal_agency_twfe_russianjudge_interacted_nonpetty_matched, fit_local_entity_twfe_russianjudge_basic_nonpetty_matched, fit_local_entity_twfe_russianjudge_interacted_nonpetty_matched, align = F, dep.var.labels = "", covariate.labels = c("Russian judge", "Appeals", "Appeals $\\times$ Russian judge"), column.labels = c("Government", "Federal agency", "Reg/mun agency", "Local entity"), column.separate = c(2), no.space = T, keep.stat = c("n", "rsq"), add.lines = list(c("Case type", rep("non-petty", 8)), c("Case FE", rep("yes", 8)), c("Judge FE", rep("no", 8))), notes = "TBA", type = "latex", style = "ajps")
cat(paste(table_twfe_all_matched, collapse = "\n"), file = "tables/tableS11_twfe_all_matched.tex")
